#include <vector>
#include <string>
#include <fstream>
#include <cmath>
#include <stdexcept>
#include <random>
#include <iostream>
#include "Network.hpp"

#ifndef BPNETWORK_HPP
#define BPNETWORK_HPP

// BP神经网络（继承自Network基类，使用ReLUNeuron）
class BPNetwork : public Network {
private:
	std::vector<std::vector<ReLUNeuron>> layers;  // 网络层（每层由ReLU神经元组成）
	double learning_rate;                         // 学习率
	
public:
	// 构造函数：指定各层神经元数量（如{2,3,1}表示2输入,3隐藏,1输出）
	BPNetwork(const std::vector<size_t>& layerSizes, double lr = 0.01) : learning_rate(lr) {
		if (layerSizes.size() < 2) {
			throw std::invalid_argument("网络至少需要输入层和输出层");
		}
		// 初始化各层神经元（从输入层之后开始构建）
		for (size_t i = 1; i < layerSizes.size(); ++i) {
			size_t inputSize = layerSizes[i - 1];  // 前一层神经元数量=当前层输入维度
			std::vector<ReLUNeuron> layer;
			for (size_t j = 0; j < layerSizes[i]; ++j) {
				layer.emplace_back(inputSize);  // 每个神经元接收前一层所有输出
			}
			layers.push_back(layer);
		}
	}
	
	// 前向传播：获取网络输出
	std::vector<double> get(const std::vector<double>& input) override {
		if (input.size() != layers[0][0].weights.size()) {
			throw std::invalid_argument("输入维度与网络不匹配");
		}
		std::vector<double> currentInput = input;
		// 逐层计算输出
		for (auto& layer : layers) {
			std::vector<double> currentOutput;
			for (auto& neuron : layer) {
				currentOutput.push_back(neuron.count(currentInput));
			}
			currentInput = currentOutput;
		}
		return currentInput;
	}
	
	// 反向传播训练（单样本）
	void train(const std::vector<double>& input, const std::vector<double>& expected) override {
		// 1. 前向传播获取输出（同时更新神经元的pre_act和post_act）
		std::vector<double> output = get(input);
		if (output.size() != expected.size()) {
			throw std::invalid_argument("期望输出维度与网络输出不匹配");
		}
		
		// 2. 计算各层误差项（从输出层到输入层反向计算）
		std::vector<std::vector<double>> deltas;  // 误差项（delta = error * 激活函数导数）
		
		// 输出层误差项
		std::vector<double> outputDeltas;
		const auto& outputLayer = layers.back();
		for (size_t i = 0; i < output.size(); ++i) {
			double error = expected[i] - output[i];  // 输出层误差=期望-实际
			double deriv = outputLayer[i].getActDeriv();  // 使用public接口获取导数
			outputDeltas.push_back(error * deriv);
		}
		deltas.push_back(outputDeltas);
		
		// 隐藏层误差项（从后往前计算）
		for (int i = layers.size() - 2; i >= 0; --i) {
			const auto& currentLayer = layers[i];
			const auto& nextLayer = layers[i + 1];
			const auto& nextDeltas = deltas[0];  // 下一层的误差项
			std::vector<double> currentDeltas;
			
			for (size_t j = 0; j < currentLayer.size(); ++j) {
				// 隐藏层误差=下一层误差项与权重乘积之和
				double error = 0.0;
				for (size_t k = 0; k < nextLayer.size(); ++k) {
					error += nextDeltas[k] * nextLayer[k].weights[j];
				}
				// 隐藏层误差项=误差 * 激活函数导数（使用public接口）
				double deriv = currentLayer[j].getActDeriv();
				currentDeltas.push_back(error * deriv);
			}
			deltas.insert(deltas.begin(), currentDeltas);  // 插入到前面（保持层顺序）
		}
		
		// 3. 更新权重和偏置（从输入层到输出层）
		std::vector<double> prevOutput = input;  // 前一层的输出（初始为输入）
		for (size_t i = 0; i < layers.size(); ++i) {
			auto& layer = layers[i];
			const auto& currentDeltas = deltas[i];
			std::vector<double> currentOutput;
			
			for (size_t j = 0; j < layer.size(); ++j) {
				auto& neuron = layer[j];
				double delta = currentDeltas[j];
				
				// 更新偏置：bias += 学习率 * delta
				neuron.bias += learning_rate * delta;
				
				// 更新权重：weight += 学习率 * delta * 前一层输出
				for (size_t k = 0; k < neuron.weights.size(); ++k) {
					neuron.weights[k] += learning_rate * delta * prevOutput[k];
				}
				
				currentOutput.push_back(neuron.post_act);  // 记录当前层输出（用于下一层更新）
			}
			prevOutput = currentOutput;
		}
	}
	
	// 保存模型到文件（二进制）
	void store(const std::string& filename) const override {
		std::ofstream file(filename, std::ios::binary);
		if (!file.is_open()) {
			throw std::runtime_error("无法打开文件保存模型");
		}
		
		// 保存学习率
		file.write(reinterpret_cast<const char*>(&learning_rate), sizeof(learning_rate));
		
		// 保存层数
		size_t numLayers = layers.size();
		file.write(reinterpret_cast<const char*>(&numLayers), sizeof(numLayers));
		
		// 保存每层信息
		for (const auto& layer : layers) {
			// 保存当前层神经元数量
			size_t numNeurons = layer.size();
			file.write(reinterpret_cast<const char*>(&numNeurons), sizeof(numNeurons));
			
			// 保存每个神经元的参数
			for (const auto& neuron : layer) {
				// 保存权重数量
				size_t numWeights = neuron.weights.size();
				file.write(reinterpret_cast<const char*>(&numWeights), sizeof(numWeights));
				
				// 保存权重
				for (double w : neuron.weights) {
					file.write(reinterpret_cast<const char*>(&w), sizeof(w));
				}
				
				// 保存偏置
				file.write(reinterpret_cast<const char*>(&neuron.bias), sizeof(neuron.bias));
			}
		}
	}
	
	// 从文件加载模型
	void load(const std::string& filename) override {
		std::ifstream file(filename, std::ios::binary);
		if (!file.is_open()) {
			throw std::runtime_error("无法打开文件加载模型");
		}
		
		// 清空现有网络
		layers.clear();
		
		// 加载学习率
		file.read(reinterpret_cast<char*>(&learning_rate), sizeof(learning_rate));
		
		// 加载层数
		size_t numLayers;
		file.read(reinterpret_cast<char*>(&numLayers), sizeof(numLayers));
		
		// 加载每层信息
		for (size_t i = 0; i < numLayers; ++i) {
			// 加载当前层神经元数量
			size_t numNeurons;
			file.read(reinterpret_cast<char*>(&numNeurons), sizeof(numNeurons));
			
			std::vector<ReLUNeuron> layer;
			// 加载每个神经元
			for (size_t j = 0; j < numNeurons; ++j) {
				// 加载权重数量（即输入维度）
				size_t numWeights;
				file.read(reinterpret_cast<char*>(&numWeights), sizeof(numWeights));
				
				// 创建神经元（先构造一个临时对象，再替换权重和偏置）
				ReLUNeuron neuron(numWeights);
				
				// 加载权重
				for (size_t k = 0; k < numWeights; ++k) {
					file.read(reinterpret_cast<char*>(&neuron.weights[k]), sizeof(neuron.weights[k]));
				}
				
				// 加载偏置
				file.read(reinterpret_cast<char*>(&neuron.bias), sizeof(neuron.bias));
				
				layer.push_back(neuron);
			}
			layers.push_back(layer);
		}
	}
	
	// 辅助接口：设置学习率
	void setLearningRate(double lr) {
		if (lr <= 0) {
			throw std::invalid_argument("学习率必须为正数");
		}
		learning_rate = lr;
	}
};
#endif